/*
    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

*/

#include "cgpanel/raytracer/bvhnode.h"
#include "cgpanel/raytracer/box.h"
#include "objects/plane.h"
#include "cgpanel/raytracer/hitrecord.h"
#include <QtAlgorithms>

#include <iostream>
using namespace std;

namespace draw{

  // 0 = x, 1= y, 2 = z
BVHNode::BVHNode(QList<object::Plane*> *A, int axis)
{
  right = NULL;
  left = NULL;

  lrec = new draw::HitRecord();
  rrec = new draw::HitRecord();
  
  bbox = NULL;
  int N = A->size();
 
  if(N==1){
    left = A->at(0);
    lrec = new draw::HitRecord();
    right = NULL;
    bbox = A->at(0)->getBoundingBox();
    //cout << "N=1"<< endl;
    //cin.get();
  }else
  if(N==2){
    left = A->at(0);
    right = A->at(1);

    bbox = left->getBoundingBox()->combine(right->getBoundingBox());
  }
  else{
    temp1.clear();
    temp2.clear();
    
    float midpoint =0;
    
    if(axis == 0){
      //sortToCentersToX(&A, A.size());
      for(int i = 0; i < A->size(); ++i){
        midpoint+=A->at(i)->getCenterX();
      }
      midpoint/=A->size();
    
      for(int i = 0; i < A->size(); ++i){
        if(A->at(i)->getCenterX() < midpoint)
          temp1.append(A->at(i));
        else
          temp2.append(A->at(i));
      }
    }
    else if (axis == 1){
      for(int i = 0; i < A->size(); ++i){
        midpoint+=A->at(i)->getCenterY();
      }
      midpoint/=A->size();
    
      for(int i = 0; i < A->size(); ++i){
        if(A->at(i)->getCenterY() < midpoint)
          temp1.append(A->at(i));
        else
          temp2.append(A->at(i));
      }
    }
    else if (axis == 2){
      for(int i = 0; i < A->size(); ++i){
        midpoint+=A->at(i)->getCenterZ();
      }
      midpoint/=A->size();
    
      for(int i = 0; i < A->size(); ++i){
        if(A->at(i)->getCenterZ() < midpoint)
          temp1.append(A->at(i));
        else
          temp2.append(A->at(i));
      }
    }
    
    if(temp1.size() == 0){
      right=new BVHNode(&temp2,(axis+1)%3);
      bbox = right->getBoundingBox();
    }
    else if(temp2.size() == 0){
      left= new BVHNode(&temp1,(axis+1)%3);
      bbox = left->getBoundingBox();
    }
    else{
      left= new BVHNode(&temp1,(axis+1)%3);
      right=new BVHNode(&temp2,(axis+1)%3); 
      bbox = left->getBoundingBox()->combine(right->getBoundingBox());
    }

    
  }
}

BVHNode::~BVHNode()
{
  if(bbox != NULL)
    delete bbox;
}



bool BVHNode::hit(draw::Ray * ray, draw::HitRecord ** rec)
{
  //qDebug("hitting...");
   if(bbox->hit(ray)){
     bool lefthit =  (left != NULL) && left->hit(ray, &lrec);
     bool righthit = (right != NULL) && right->hit(ray, &rrec);
     if(righthit && lefthit/* && rrec->t() != 0 && lrec->t() != 0*/){
       if(lrec->t() < rrec->t()){
         *rec = lrec;
       }
       else{
         *rec = rrec;
       }
      return true; 
     }else if(lefthit /*&& lrec->t()*/){
       *rec = lrec;
       return true;
     }else if(righthit/* && rrec->t()*/){
       *rec = rrec;
       return true;
     } else{
       return false;
     }       
   }else{
    return false; 
   }
}

bool BVHNode::hit(draw::Ray * ray, float tMax)
{
   if(bbox->hit(ray)){
     bool lefthit = (left != NULL) && left->hit(ray, tMax);
     if(lefthit)
       return true;
     bool righthit = (right != NULL) && right->hit(ray, tMax);
     if(righthit)
       return true;
     
     return false;     
   }
   return false; 
}

};